package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.WemediaConstants;
import com.heima.common.constants.WmNewsMessageConstants;
import com.heima.common.exception.CustomException;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dtos.WmNewsAuthDto;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.model.wemedia.vo.WmNewsVo;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsAutoScanService;
import com.heima.wemedia.service.WmNewsService;
import com.heima.wemedia.service.WmNewsTaskService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    //文章内容审核
    @Autowired
    private WmNewsAutoScanService wmNewsAutoScanService;

    //操作文章素材关联表
    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;
    //素材表
    @Autowired
    private WmMaterialMapper wmMaterialMapper;
    //延时任务
    @Autowired
    private WmNewsTaskService wmNewsTaskService;

    /**
     * 自媒体文章条件,分页查询
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult findAll(WmNewsPageReqDto dto) {
        //1.检查参数
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //分页参数效验
        dto.checkParam();
        //登陆人获取
        WmUser user = WmThreadLocalUtil.getUser();
        if (user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        //2.分页条件查询
        Page<WmNews> page = new Page<>(dto.getPage(), dto.getSize());
        //条件查询构造器
        LambdaQueryWrapper<WmNews> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //状态精确查询
        if (dto.getStatus() != null) {
            lambdaQueryWrapper.eq(WmNews::getStatus, dto.getStatus());
        }
        //频道精确查询
        lambdaQueryWrapper.eq(dto.getChannelId() != null, WmNews::getChannelId, dto.getChannelId());
        //时间范围查询
        lambdaQueryWrapper.between(dto.getBeginPubDate() != null && dto.getEndPubDate() != null,
                WmNews::getPublishTime, dto.getBeginPubDate(), dto.getEndPubDate());
        //查询当前登录用户的文章
        lambdaQueryWrapper.eq(WmNews::getUserId, user.getId());
        //发布时间倒序查询
        lambdaQueryWrapper.orderByDesc(WmNews::getPublishTime);
        page = this.page(page, lambdaQueryWrapper);

        ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(), (int) page.getTotal());

        responseResult.setData(page.getRecords());


        return responseResult;
    }


    /**
     * 自媒体提交文章或保存为草稿,或更改文章
     * 后台判断请求中是否包含了文章id,如果包含了id，则为修改请求,如果不包含id,则为新增
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult submitNews(WmNewsDto dto) {

        //0.条件判断,提交的对象dto与文章内容不为空
        if (dto == null || dto.getContent() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //1保存或修改文章
        //创建文章对象
        WmNews wmNews = new WmNews();
        //将dto数据拷贝wmNews
        BeanUtils.copyProperties(dto, wmNews);

        //封面图片属性无法拷贝,处理后 set填入
        if (dto.getImages() != null && dto.getImages().size() > 0) {
            //[1dddfsd.jpg,sdlfjldk.jpg]-->   1dddfsd.jpg,sdlfjldk.jpg
            String imageStr = StringUtils.join(dto.getImages(), ",");
            wmNews.setImages(imageStr);
        }
        //如果当前封面类型为自动 -1数据库表中没有-1,设为空
        if (WemediaConstants.WM_NEWS_TYPE_AUTO.equals(dto.getType())) {
            wmNews.setType(null);
        }


        //保存或修改文章方法
        saveOrUpdateWmNews(wmNews);

        //2.判断是否为草稿 dto.getStatus()==0
        if (dto.getStatus().equals(WmNews.Status.NORMAL.getCode())) {
            //结束该方法,草稿不用处理文章材料关系
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }


        //3.不是草稿,保存文章内容图片与素材关系
        //获取到文章内容中的图片信息imagesUrl,集合
        List<String> materials = ectractUrlInfo(dto.getContent());


        //保存文章内容图片与素材关系,需要文章Id,和图片路径信息
        saveRelativeInfoForContent(materials, wmNews.getId());


        //4.不是草稿,保存文章封面图片与素材关系,当前布局是自动需要匹配图片
        //dto,存有封面图片wmNews可能会修改封面图片路径materials自动布局,需要内容中的图片
        saveRelativeInfoForCover(dto, wmNews, materials);


        //文章自动审核

        // wmNewsAutoScanService.autoScanWmNews(wmNews.getId());
        //文章自动审核,调用延时任务,存入延时队列
        wmNewsTaskService.addNewsToTask(wmNews.getId(), wmNews.getPublishTime());


        //返回文章 保存数据库成功
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }


    /**
     * 第一个功能：如果当前封面类型为自动，则设置封面类型的数据
     * 匹配规则：
     * 1，如果内容图片大于等于1，小于3  单图  type 1
     * 2，如果内容图片大于等于3  多图  type 3
     * 3，如果内容没有图片，无图  type 0
     * <p>
     * 第二个功能：保存封面图片与素材的关系
     *
     * @param dto
     * @param wmNews
     * @param materials
     */
    public void saveRelativeInfoForCover(WmNewsDto dto, WmNews wmNews, List<String> materials) {
        //取出文章封面图片
        List<String> images = dto.getImages();
        //如果当前封面类型为自动,
        // 则images文章封面图片没有值，需要设置封面类型的数据,然后根据类型,用文章内容图片,给文章封面图片赋值,
        //文章封面有素材值,则封面类型的数据由前端传入
        if (WemediaConstants.WM_NEWS_TYPE_AUTO.equals(dto.getType())) {
            //文章内容图片大于3
            if (materials.size() >= 3) {
                //设置文章布局为多图
                wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
                //将内容图片的前3张给,封面图片
                images = materials.stream().limit(3).collect(Collectors.toList());
            } else if (materials.size() >= 1 && materials.size() < 3) {
                //文章内容图片大于1小于3
                //设置文章布局为单图
                wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
                //将内容图片的前3张给,封面图片
                images = materials.stream().limit(1).collect(Collectors.toList());
            } else {
                //文章无图
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            }

            //文章布局属性改变修改数据库
            if (images != null && images.size() > 0) {
                //文章内容不为无图,将
                wmNews.setImages(StringUtils.join(images, ","));
            }
            updateById(wmNews);

        }
        //文章封面选着自动,文章类容有图赋值给了封面.或者文章封面本身就有图
        if (images != null && images.size() > 0) {
            //将文章封面素材与文章绑定,上传数据库
            saveRelativeInfo(images, wmNews.getId(), WemediaConstants.WM_COVER_REFERENCE);
        }

    }

    /**
     * 处理文章内容图片与素材的关系
     *
     * @param materials
     * @param newsId
     */
    public void saveRelativeInfoForContent(List<String> materials, Integer newsId) {

        //通用 保存图片与文章的关系 WemediaConstants.WM_CONTENT_REFERENCE 表示图签属于 是类容还是封面
        saveRelativeInfo(materials, newsId, WemediaConstants.WM_CONTENT_REFERENCE);


    }

    /**
     * 保存文章图片与素材的关系到数据库中
     *
     * @param materials
     * @param newsId
     * @param type
     */
    public void saveRelativeInfo(List<String> materials, Integer newsId, Short type) {

        //判断materials是否为空
        if (materials != null && materials.size() > 0) {


            //根据素材url路径找素材id
            List<WmMaterial> dbMaterials = wmMaterialMapper.selectList(Wrappers
                    .<WmMaterial>lambdaQuery().
                            in(WmMaterial::getUrl, materials));

            //草稿时,可以删除素材,所以要判断素材是否有效,素材是否有所减少,

            //判断素材是否有效
            if (dbMaterials == null || dbMaterials.size() == 0) {
                //手动抛出异常   第一个功能：能够提示调用者素材失效了，第二个功能，进行数据的回滚
                throw new CustomException(AppHttpCodeEnum.MATERIASL_REFERENCE_FAIL);
            }
            //数量是否相同
            if (materials.size() != dbMaterials.size()) {
                //抛异常功能,1.提示失效,2.进行数据回滚
                throw new CustomException(AppHttpCodeEnum.MATERIASL_REFERENCE_FAIL);
            }


            //取出dbMaterials中的每一个id
            List<Integer> idList = dbMaterials.stream().map(WmMaterial::getId).collect(Collectors.toList());

            //批量保存
            wmNewsMaterialMapper.saveRelations(idList, newsId, type);

        }


    }

    /**
     * 提取文章内容中的图片信息
     *
     * @param content
     * @return
     */
    public List<String> ectractUrlInfo(String content) {

        //创建图片信息集合
        List<String> materials = new ArrayList<>();
        //将文章内容转换为map对象
        List<Map> maps = JSON.parseArray(content, Map.class);
        //遍历list集合获取每一个map对象,
        for (Map map : maps) {
            //判断map对象中的"type"是否为"image"
            if ("image".equals(map.get("type"))) {
                //是则取出键为"value"的值
                String imgUrl = (String) map.get("value");
                //加入materials
                materials.add(imgUrl);

            }
        }


        return materials;
    }

    /**
     * 保存或修改文章
     *
     * @param wmNews
     */
    public void saveOrUpdateWmNews(WmNews wmNews) {
        //补全属性保存到数据库
        wmNews.setUserId(WmThreadLocalUtil.getUser().getId());
        wmNews.setCreatedTime(new Date());
        wmNews.setSubmitedTime(new Date());
        wmNews.setEnable((short) 1);//默认上架
        //判断是保存,还是修改数据
        if (wmNews.getId() == null) {
            //前端没有发id保存
            save(wmNews);
        } else {
            //前端传入文章ID修改文章
            //秀给前先删除文章图片与素材的关系,后面在同一添加
            wmNewsMaterialMapper.delete(
                    Wrappers.<WmNewsMaterial>lambdaQuery()
                            //根据文章id进行删除
                            .eq(WmNewsMaterial::getNewsId, wmNews.getId())
            );
            //修改文章数据
            updateById(wmNews);


        }
    }

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    /**
     * 文章的上下架
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult downOrUp(WmNewsDto dto) {
        //1.检查参数
        if (dto.getId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.查询文章
        WmNews wmNews = getById(dto.getId());
        if (wmNews == null) {
            ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }
        //3.判断文章是否已发布文章状态是否是9
        if (!wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "当前文章不是发布状态，不能上下架");
        }
        //4.修改文章enable
        if (dto.getEnable() != null && (dto.getEnable() == 0 || dto.getEnable() == 1)) {
            update(Wrappers.<WmNews>lambdaUpdate()
                    .set(WmNews::getEnable, dto.getEnable())
                    .eq(WmNews::getId, dto.getId())
            );

            //调用kafka发送文章上下架消息,通知article端修改文章配置
            if (wmNews.getArticleId() == null) {
                Map<String, Object> map = new HashMap<>();
                //封装文章的id和上下架状态,发送到app文章端
                map.put("articleId", wmNews.getArticleId());
                map.put("enable", dto.getEnable());
                //kafka发送消息
                kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC, JSON.toJSONString(map));
            }
        }

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 自媒体文章审核列表
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult list_vo(WmNewsPageReqDto dto) {
        //1.参数检查
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //分页参数效验
        dto.checkParam();
        //登陆人获取
        Page<WmNews> page = new Page<>(dto.getPage(), dto.getSize());
        LambdaQueryWrapper<WmNews> queryWrapper = new LambdaQueryWrapper<>();
        if (dto.getStatus() != null) {
            queryWrapper.eq(WmNews::getStatus, dto.getStatus());
        }
        if (dto.getTitle() != null && dto.getTitle() != "") {
            queryWrapper.like(WmNews::getTitle, dto.getTitle());
        }
        queryWrapper.orderByDesc(WmNews::getCreatedTime);
        page = page(page, queryWrapper);

        ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(), (int) page.getTotal());
        responseResult.setData(page.getRecords());


        return responseResult;
    }


    @Autowired
    private WmUserMapper wmUserMapper;

    /**
     * 自媒体文章审核详细信息
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult one_vo(Integer id) {

        //1.检查参数
        if (id == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //查询文章信息
        WmNews wmNews = this.getById(id);
        if (wmNews == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        WmNewsVo wmNewsVo = new WmNewsVo();
        BeanUtils.copyProperties(wmNews, wmNewsVo);
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());

        if (wmUser != null) {

            wmNewsVo.setAuthorName(wmUser.getName());
        }

        return ResponseResult.okResult(wmNewsVo);
    }

    /**
     * 自媒体文章审核失败
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult auth_fail(WmNewsAuthDto dto) {
        //1.参数检查
        if (dto == null || dto.getId() == null || dto.getStatus() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.根据id查询用户
        WmNews wmNews = this.getById(dto.getId());
        if (wmNews == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //设置审核状态改为失败
        wmNews.setStatus(dto.getStatus());
        //设置失败原因
        wmNews.setReason(dto.getMsg());


        this.updateById(wmNews);


        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }


    /**
     * 自媒体文章审核成功
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult auth_pass(WmNewsAuthDto dto) {
        //1.参数检查
        if (dto == null || dto.getId() == null || dto.getStatus() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.根据id查询用户
        WmNews wmNews = this.getById(dto.getId());
        if (wmNews == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //设置审核状态改为失败
        wmNews.setStatus(dto.getStatus());

        this.updateById(wmNews);

        //app端保存文章
        //根据文章设置app端相关的文章数据,并远程调用保存app端文章相关数据
        ResponseResult responseResult = wmNewsAutoScanService.saveAppArticle(wmNews);
        return responseResult;
    }

}
